import datetime
import time
import pandas as pd
from functools import partial

from sanic import Sanic, json
from sanic.response import text
from sanic.worker.loader import AppLoader
from xtquant import xtconstant, xtdata
from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from xtquant.xttype import StockAccount

# 账户配置
account = StockAccount('11010570038', 'CREDIT')
session_id = int(time.time())

def handle_api_error(func):
    """API错误处理装饰器"""
    async def wrapper(*args, **kwargs):
        try:
            return await func(*args, **kwargs)
        except Exception as e:
            return json({"error": str(e), "api": func.__name__})
    wrapper.__name__ = func.__name__  # 保持原函数名
    return wrapper

class MyXtQuantTraderCallback(XtQuantTraderCallback):
    """交易回调处理类"""
    
    def on_disconnected(self):
        """连接断开回调"""
        print(datetime.datetime.now(), '连接断开回调')

    def on_stock_order(self, order):
        """委托回报推送"""
        print(datetime.datetime.now(), '委托回调 投资备注', order.order_remark)

    def on_stock_trade(self, trade):
        """成交变动推送"""
        print(datetime.datetime.now(), '成交回调', trade.order_remark,
              f"委托方向(48买 49卖) {trade.offset_flag} 成交价格 {trade.traded_price} 成交数量 {trade.traded_volume}")

    def on_order_error(self, order_error):
        """委托失败推送"""
        print(f"委托报错回调 {order_error.order_remark} {order_error.error_msg}")

    def on_cancel_error(self, cancel_error):
        """撤单失败推送"""
        print(datetime.datetime.now(), '撤单失败')

    def on_order_stock_async_response(self, response):
        """异步下单回报推送"""
        print(f"异步委托回调 投资备注: {response.order_remark}")

    def on_cancel_order_stock_async_response(self, response):
        """撤单异步回报推送"""
        print(datetime.datetime.now(), '撤单异步回报')

    def on_account_status(self, status):
        """账户状态推送"""
        print(datetime.datetime.now(), '账户状态变更')

def create_app():
    """创建Sanic应用实例"""
    try:
        app = Sanic("MyHelloWorldApp")

        def connect():
            """建立交易连接"""
            global session_id
            path = 'C:\\国金证券QMT交易端\\userdata_mini'
            session_id += 1
            
            xt_trader = XtQuantTrader(path, session_id)
            callback = MyXtQuantTraderCallback()
            xt_trader.register_callback(callback)
            xt_trader.start()
            
            return (xt_trader, True) if xt_trader.connect() == 0 else (None, False)

        @app.listener("before_server_start")
        async def setup_db(app):
            """服务启动前初始化"""
            xt_trader, success = connect()
            app.ctx.xt_trader = xt_trader
            print('success' if success else "error")

        @app.get("/")
        async def hello_world(request):
            return text("Hello, world.")

        @app.get("/order_stock", name="order_stock")
        @handle_api_error
        async def order_stock(request):
            """股票委托下单"""
            code = request.args.get("code")
            order_type = int(request.args.get("order_type"))
            count = int(request.args.get("count"))
            price = 1 if order_type == 24 else 9999
            
            result = request.app.ctx.xt_trader.order_stock_async(
                account=account,
                stock_code=code,
                order_type=order_type,
                order_volume=count,
                price_type=xtconstant.MARKET_PEER_PRICE_FIRST,
                price=price,
                strategy_name='strategy_name',
                order_remark='remark'
            )
            return text(str(result))

        @app.get("/query_stock_positions", name="query_stock_positions")
        @handle_api_error
        async def query_stock_positions(request):
            """查询股票持仓"""
            positions = request.app.ctx.xt_trader.query_stock_positions(account)
            return json([
                {
                    'stock_code': pos.stock_code,
                    'volume': pos.volume,
                    'can_use_volume': pos.can_use_volume,
                    'open_price': pos.open_price,
                    'market_value': pos.market_value
                }
                for pos in positions if pos.volume != 0
            ])

        @app.get("/query_stock_asset", name="query_stock_asset")
        @handle_api_error
        async def query_stock_asset(request):
            """查询股票资产"""
            asset = request.app.ctx.xt_trader.query_stock_asset(account)
            return json({"cash": asset.cash})

        @app.get("/query_credit_detail", name="query_credit_detail")
        @handle_api_error
        async def query_credit_detail(request):
            """查询信用账户明细"""
            credit_details = request.app.ctx.xt_trader.query_credit_detail(account)
            detail = credit_details[0]
            return json({
                'm_dBalance': detail.m_dBalance,
                'm_dAvailable': detail.m_dAvailable,
                'm_dPositionProfit': detail.m_dPositionProfit,
                'm_dMarketValue': detail.m_dMarketValue,
                'm_dFetchBalance': detail.m_dFetchBalance,
                'm_dStockValue': detail.m_dStockValue,
                'm_dTotalDebt': detail.m_dTotalDebt,
                'm_dEnableBailBalance': detail.m_dEnableBailBalance,
                'm_dPerAssurescaleValue': detail.m_dPerAssurescaleValue,
                'm_dAssureAsset': detail.m_dAssureAsset
            })

        @app.get("/get_full_tick", name="get_full_tick")
        @handle_api_error
        async def get_full_tick(request):
            """获取完整逐笔成交信息"""
            code_list = request.args.get("code_list", "").split(",")
            if not code_list:
                return json({"error": "code_list is required"})
                
            tick_data = xtdata.get_full_tick(code_list)
            return json([{**tick_data[code], "name": code} for code in tick_data])

        
        def my_download(stock_list,period,start_date = '', end_date = ''):
          '''
          用于显示下载进度
          '''
          if "d" in period:
            period = "1d"
          elif "m" in period:
            if int(period[0]) < 5:
              period = "1m"
            else:
              period = "5m"
          elif "tick" == period:
            pass
          else:
            raise KeyboardInterrupt("周期传入错误")


          n = 1
          num = len(stock_list)
          for i in stock_list:
            print(f"当前正在下载{n}/{num}")
            
            xtdata.download_history_data(i,period,start_date, end_date)
            n += 1
          print("下载任务结束")
        

        @app.get("/get_market_data_ex", name="get_market_data_ex")
        @handle_api_error
        async def get_market_data_ex(request):
            code_list = request.args.get("code_list", "").split(",")
            start_date= request.args.get("start_date", "")
            end_date = "" 
            period = "1d" 
            my_download(code_list, period, start_date, end_date)
            
            tick_data= xtdata.get_market_data_ex(field_list=[], stock_list=code_list, period=period, count=20, dividend_type='front', fill_data=True)
            result = []
            for key, value in tick_data.items():
                result.append({"code": key, "list": value.to_json()})
            return json(result)

        @app.get("/get_turnover_rate", name="get_turnover_rate")
        @handle_api_error
        async def get_turnover_rate(request):
            """
            获取换手率
            参数说明:
            - code_list: 股票代码列表
            """
            code_list = request.args.get("code_list", "").split(",")
            if not code_list:
                return json({"error": "code_list is required"})
            
            tick_data = xtdata.get_full_tick(code_list)
            return json([
                {
                    "code": code,
                    "turnover_rate": tick_data[code]["turnover_rate"]
                }
                for code in tick_data
            ])

        @app.get("/get_longhubang", name="get_longhubang")
        @handle_api_error
        async def get_longhubang(request):
            """
            获取龙虎榜数据
            参数说明:
            - code_list: 股票代码列表
            - start_time: 开始时间
            - end_time: 结束时间
            """
            code_list = request.args.get("code_list", "").split(",")
            if not code_list:
                return json({"error": "code_list is required"})
            
            params = {
                "stock_list": code_list,
                "start_time": request.args.get("start_time", ""),
                "end_time": request.args.get("end_time", "")
            }
            
            data = xtdata.get_longhubang(**params)
            return json(data)

        @app.get("/get_financial_data", name="get_financial_data")
        @handle_api_error
        async def get_financial_data(request):
            """
            获取财务数据
            参数说明:
            - code_list: 股票代码列表
            - indicators: 财务指标列表
            - start_time: 开始时间
            - end_time: 结束时间
            - report_type: 报告类型
            """
            code_list = request.args.get("code_list", "").split(",")
            if not code_list:
                return json({"error": "code_list is required"})
            
            params = {
                "stock_list": code_list,
                "field_list": request.args.get("indicators", "").split(","),
                "start_time": request.args.get("start_time", ""),
                "end_time": request.args.get("end_time", ""),
                "report_type": request.args.get("report_type", "")
            }
            
            data = xtdata.get_financial_data(**params)
            return json(data)

        @app.get("/get_last_volume", name="get_last_volume")
        @handle_api_error
        async def get_last_volume(request):
            """
            获取最新流通股本
            参数说明:
            - code_list: 股票代码列表
            """
            code_list = request.args.get("code_list", "").split(",")
            if not code_list:
                return json({"error": "code_list is required"})
            
            data = xtdata.get_last_volume(stock_list=code_list)
            return json(data)

        @app.get("/get_total_share", name="get_total_share")
        @handle_api_error
        async def get_total_share(request):
            """
            获取总股数
            参数说明:
            - code_list: 股票代码列表
            """
            code_list = request.args.get("code_list", "").split(",")
            if not code_list:
                return json({"error": "code_list is required"})
            
            data = xtdata.get_total_share(stock_list=code_list)
            return json(data)

        return app
        
    except Exception as e:
        print(f"Application creation failed: {e}")
        exit()

if __name__ == "__main__":
    loader = AppLoader(factory=partial(create_app))
    app = loader.load()
    app.prepare(host="0.0.0.0", port=8888, dev=True)
    Sanic.serve(primary=app, app_loader=loader)
